# 导包
import hashlib
import json
import re
import time
import datetime

from datetime import tzinfo
from functools import wraps
from operator import mod
from os import write

import requests
from peewee import EXCEPTIONS, NoopModelSelect
from redis.client import Redis
import tornado.web

from bson.objectid import ObjectId

# 服务器
from tornado import httpserver
from werkzeug.datastructures import Headers

# 导入基础类
from base import BaseHandler, WsBaseHandler

# 导入数据库操作类
from db import *

from tools import ret_json, Chat, ACL, RBAC, ABAC, BinAuth, OrderQueue, table_size, clear_last

import jwt, os

# 导入栈列
from test_stack import MyStack

# python内置装饰器库
from functools import wraps

# 导入队列类mysql                    mongo
from test_queue import ChatQueue, MyQueueSort, MyMongo_QueueSort, RedisQueue, MysqlDb, get_queue, DelayRedisQueue

# 导入多线程队列
from out_queue import doout
import threading

import random

import redis

# 导入分页类
from mypage import Page

# 密->明    明->密  双向
import base64

from pay import AliPay

# 导入锁
from test_setnx import distributed_lockup, distributed_release_locks

# 导入伪随机
from test_random import weight_choic

# 导入可视化柱状图方法
from test_mat import plt_png

import socket

set_type = {'图片': {'.png', '.jpg'}, '视频': {'.mp4'}, '音频': {'.mp3'}}

ad_type = {'type': {'pv': '报告pv', 'uv': '报告uv'},
           'time': {'month': {'sql': "DATE_FORMAT(date,'%Y%m') = DATE_FORMAT(CURDATE(),'%Y%m')", 'type': '月'},
                    'week': {'sql': "YEARWEEK(DATE_FORMAT(date,'%Y-%m-%d')) = YEARWEEK(NOW())", 'type': '周'},
                    'day': {'sql': "to_days(date) = to_days(now())", 'type': '日'}}}

day = datetime.date.today()

# str(day.today()) + ad_type['type']['pv']

import requests

import random
import time
import hashlib

import qrcode
from bs4 import BeautifulSoup


class Ads_Reporting(BaseHandler):

    async def get(self):
        time = self.get_argument('time', None)

        name = ad_type['time'][time]

        # sql = "select a.uip,a.uua as uv,b.pv,a.adid,c.title from (SELECT count(DISTINCT uip) as uip ,count(DISTINCT uua) as uua , adid FROM `ad_recording`  where to_days(date) = to_days(now()) group by adid ) as a inner join (select count(*) as pv ,adid from `ad_recording` group by adid ) as b on a.adid = b.adid inner join ad_publishing as c on a.adid = c.id;"

        sql = "select a.uv,a.pv,a.adid,b.title from (SELECT count(DISTINCT uua) as uv,count(uua) as pv , adid FROM `ad_recording` where %s group by adid ) as a inner join ad_publishing as b on a.adid = b.id ;" % \
              name['sql']

        mysql = MysqlDb()
        res = mysql.read_list(sql)
        uv = []
        pv = []
        title = []
        for i in res:
            uv.append(i['uv'])
            pv.append(i['pv'])
            title.append(i['title'])

        plt_png(title, pv, 'pv分数排行', '%s分析柱状表' % day, '个数', '广告名', '当%spv图' % name['type'])
        plt_png(title, uv, 'uv分数排行', '%s分析柱状表' % day, '个数', '广告名', '当%suv图' % name['type'])


class Ad(BaseHandler):
    # 获取用户选择的广告形式类型
    async def get(self):
        id = self.get_argument('id', None)
        type = self.get_argument('type', None)
        file_list = [{'filename': i, 'type': os.path.splitext(i)[1]} for i in [i for i in os.listdir('./static/') if
                                                                               re.match('\d*', i).group() == id and
                                                                               os.path.splitext(i)[1] in set_type[
                                                                                   type]]]

        print(file_list, id, type)

        return self.write(ret_json(file_list))

    async def post(self):
        ad = self.get_argument('ad', None)
        ad = eval(ad)
        print(ad)
        title = ad['title']
        uid = ad['id']
        description = ad['description']
        ad_form = ad['ad_form']
        ad_filename = ad['filename']
        ad_filename_type = ad['filename_type'].strip('.')
        ad_location = ad['ad_location']
        ad_industry = ad['ad_industry']
        time = ad['time']
        # print(time,'时间',type(time))
        ad_type = ad['ad_type']
        expenses = ad['expenses']
        address = ad['address']
        cost = ad['cost']
        bili = ad['bili']
        mysql = MysqlDb()
        # time = datetime.strftime(ad['time'], '%Y-%m-%d %H:%M:%S')
        # print(time)
        sql = "insert into `ad_publishing` (title,uid,description,ad_form,ad_filename,ad_filename_type,ad_location,ad_industry,time,state,ad_type,expenses,address,cost,bili) values ('%s',%s,'%s','%s','%s','%s','%s','%s','%s',%s,'%s',%s,'%s',%s,%s)" % (
            title, uid, description, ad_form, ad_filename, ad_filename_type, ad_location, ad_industry, time, 0, ad_type,
            expenses, address, cost, int(bili))
        print(sql)
        mysql.insert(sql)
        return self.write(ret_json({'code': 200, 'meg': '发布成功!'}))


class AdAll(BaseHandler):
    async def get(self):
        # // 广告形式类型图片, 视频, 音频
        ad_form = self.get_argument('ad_form', None)
        # // 广告行业
        ad_industry = self.get_argument('ad_industry', None)

        sql = "select * from ad_publishing"
        if ad_form and ad_industry:
            sql += " where ad_form = '%s' and ad_industry = '%s'" % (ad_form, ad_industry)
        elif ad_form:
            sql += " where ad_form = '%s'" % ad_form
        elif ad_industry:
            sql += " where ad_industry = '%s'" % ad_industry

        mysql = MysqlDb()
        res = mysql.read_list(sql)
        return self.write(ret_json(res))

    async def post(self):
        checkboxlist = self.get_argument('checkboxlist', None)
        list1 = eval(checkboxlist)
        mysql = MysqlDb()
        for i in list1:
            try:
                get_data = mysql.read("select * from `ad_publishing` where id = %s" % i)
                print(get_data['bili'])
                sql = "insert into ad_show (weight,adid) values (%s,%s)" % (get_data['bili'], i)
                mysql.insert(sql)
            except Exception as e:
                pass

        return self.write(ret_json({'code': 200, 'meg': '添加成功'}))

    async def put(self):
        sql = "select * from `ad_show` order by weight desc;"
        mysql = MysqlDb()
        res = mysql.read_list(sql)
        lists = []
        weight = []
        for i in res:
            weight.append(i['weight'])
            lists.append(i['adid'])
        index = weight_choic(weight)
        ad = "select * from `ad_publishing` where id = %s" % lists[index]
        meg = mysql.read(ad)
        return self.write(ret_json(meg))

    async def delete(self):
        adid = self.get_argument('adid', None)
        user_agent = self.request.headers['User-Agent']
        hostname = socket.gethostname()
        uip = socket.gethostbyname(hostname)
        date = datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d %H:%M:%S')
        mysql = MysqlDb()
        sql = "insert into `ad_recording` (uip,uua,adid,date) values ('%s','%s',%s,'%s')" % (
            uip, user_agent, adid, date)
        mysql.insert(sql)
        mysql.update("update `ad_publishing` set count = count + %s where id = %s" % (1, adid))
        return self.write(ret_json({'code': 200, 'meg': '记录成功!'}))


# vip入队出队RedisQueue
class Panduan(BaseHandler):
    async def get(self):
        id = self.get_argument('id', None)
        mydb = MysqlDb()
        res = mydb.read("select * from `test_user` where id = %s" % id)
        mydb.update("update `test_user` set ok = %s where id = %s" % (res['ok'] + 1, id))
        ausend = mydb.read("select * from `test_user` where auid = %s" % res['id'])
        r = redis.Redis(decode_responses=True)
        # r.delete('',)

        return self.write(ret_json({'code': 200, 'meg': 'ok'}))


# 信息入队
class Test_Send(BaseHandler):
    async def post(self):
        id = self.get_argument('id', None)
        role = self.get_argument('role', None)
        texts = self.get_argument('texts', None)
        r = redis.Redis(decode_responses=True)
        # 普通用户
        rq = RedisQueue('testrq')
        # 信息
        r.set("testsend%s" % id, texts)
        # 会员
        res = r.lrange('test_vip', 0, -1)
        res_vip = r.lrange('testrq', 0, -1)
        # print(res, res_vip)
        if str(id) in res:
            return self.write(ret_json({'code': 403, 'meg': '请不要重复提交'}))
        if id in res_vip:
            return self.write(ret_json({'code': 403, 'meg': '请不要重复提交'}))
        print(role)
        if role == '会员':
            rq.put_vip('test_vip', int(id))
        else:
            rq.put_r(int(id))

        return self.write(ret_json({'code': 200, 'meg': '入队成功'}))

    async def get(self):
        id = self.get_argument('id', None)
        mydb = MysqlDb()
        res = mydb.read("select * from `test_role_user` where userid = %s" % id)
        resauid = mydb.read("select * from `test_user` where auid = %s" % id)
        if not resauid:
            return self.write(ret_json({'code': 403, 'meg': ''}))
        if res['roleid'] == 3:
            r = redis.Redis(decode_responses=True)
            send = r.get('testsend%s' % resauid['id'])
            dicts = {'send': send, 'uid': resauid['id']}
            return self.write(ret_json({'code': 200, 'meg': dicts}))
        return self.write(ret_json({'code': 403, 'meg': ''}))


class Test_login(BaseHandler):
    async def get(self):
        username = self.get_argument('username', None)
        password = self.get_argument('password', None)

        mydb = MysqlDb()
        user = mydb.read("select * from `test_user` where username = %s and password = %s" % (username, password))
        if user:
            res = mydb.read("select * from `test_role_user` where userid = %s" % user['id'])

            roles = mydb.read("select * from `test_role` where id = %s" % res['roleid'])

            user['role'] = roles['role']

            # print(user)

            return self.write(ret_json({'code': 200, 'meg': user}))
        return self.write(ret_json({'code': 403, 'meg': '账密错误'}))


#############################################################################################################
# 充值接口
class SetMoney(BaseHandler):
    async def post(self):
        uid = self.get_argument('uid', None)
        price = self.get_argument('price', None)

        # 加锁
        if distributed_lockup('nx' + uid) == 1:

            try:
                # 异步开启事务        # 保证事务的隔离性
                # async with self.application.objects.atomic():

                mydb = MysqlDb()

                mydb.update("update `user` set wallet = wallet + %s where id = %s" % (price, uid))

                # 查询用户
                user = await self.application.objects.get(User.select().where(User.id == uid))

                # 累加充值金额
                user.wallet += int(price)

                # 异步修改
                await self.application.objects.update(user)

            except Exception as e:
                print(e)

            finally:
                # 释放锁
                distributed_release_locks('nx' + uid)

        return self.write('ok')


# 初始化支付类
# 封装方法
def get_ali_pay():
    alipay = AliPay(

        # 应用id
        # appid='2016092600603658',
        appid='2021000118600337',

        # 回调网址
        app_notify_url='https://localhost/pay_back/',
        return_url='https://localhost/pay_back/',

        # 商户私钥
        app_private_key_path='./keys/private.txt',

        # 支付宝公钥
        alipay_public_key_path='./keys/public.txt',

        # 调试模式
        debug=True

    )

    return alipay


# 支付宝提现接口
class AliPay_Withdrawals(BaseHandler):
    async def get(self):
        id = self.get_argument('id', None)
        mydb = MysqlDb()
        res = mydb.read("select * from `user` where id = %s" % id)
        return self.write(ret_json({'code': 200, 'meg': res['wallet']}))

    async def post(self):
        uid = self.get_argument('id', None)
        amount = self.get_argument('amount', None)
        mydb = MysqlDb()
        res = mydb.read('select * from `user` where id = %s' % uid)
        # def withdrawals(self, out_biz_no, payee_type, payee_account, amount):
        print(res['wallet'], amount)
        if res['wallet'] < int(amount):
            return self.write(ret_json({'code': 403, 'meg': '禁止篡改数据'}))
        res = distributed_lockup('withdrawals' + uid)
        if res == 1:
            try:
                mydb.update("update `user` set wallet = wallet - %s where id = %s" % (amount, uid))
                get_ali_pay().withdrawals(int(time.time()), 'ALIPAY_LOGONID', 'ldyxmb3255@sandbox.com',
                                          int(amount))

            except Exception as e:
                print(e)
            finally:
                distributed_release_locks('withdrawals' + uid)

        return self.write(ret_json({'code': 200, 'meg': '提现成功'}))


# 支付接口

class PayAli(BaseHandler):

    async def get(self):
        orderid = self.get_argument('orderid', None)
        price = self.get_argument('price', None)
        uid = self.get_argument('uid', None)

        # 初始化支付宝实例
        alipay = get_ali_pay()

        # 生成支付宝的支付地址     支付标题   订单号   价格
        # 加密参数
        params = alipay.direct_pay('test', orderid, price)
        # url地址携带参数
        url = 'https://openapi.alipaydev.com/gateway.do?%s' % params

        # 更改状态位支付中
        name = hash(int(orderid)) % table_size
        mydb = MysqlDb()
        mydb.update("update order_%s set state = 3 where `orderid`=%s " % (name + 1, orderid))

        mydb.update("update `user` set wallet = wallet + %s where id = %s" % (price, uid))

        return self.write(url)


class Pay_Back(BaseHandler):
    async def get(self):
        orderid = self.get_argument('out_trade_no', None)
        # price = self.get_argument('out_trade_no', None)
        name = hash(int(orderid)) % table_size
        mydb = MysqlDb()
        mydb.update("update order_%s set state=1 where `orderid`=%s " % (name + 1, orderid))

        self.redirect('http://localhost:8080/userinfo')


class OrderInsert(BaseHandler):

    # 订单校验接口
    async def put(self):
        id = self.get_argument('id', None)

        # 通过某种算法,加密一个密钥

        # 加入混淆  时间戳

        mytime = datetime.datetime.now().strftime('%M')

        mystr = '123_%s%s' % (id, mytime)

        # 转码
        mystr = mystr.encode(encoding='utf-8')
        # 编码
        mystr = base64.b64encode(mystr)

        # print(mystr)

        r = redis.Redis()

        r.set('token%s' % id, mystr)
        return self.write('ok')

    async def post(self):
        id = self.get_argument('id', None)
        price = self.get_argument('price', None)
        # 标识位密钥
        mytoken = self.get_argument('mytoken', None)

        r = redis.Redis(decode_responses=True)

        oq = OrderQueue('order')
        mystack = MyStack()
        slen = mystack.len(id)
        # 判断算法密钥是否一致

        # print(mytoken[:-3],mytoken,r.get('token%s' % id))

        # if mytoken[:-3] != r.get('token%s' % id):
        #     return self.write(ret_json({'code': 403, 'meg': '非法操作'}))
        if slen != 3:
            return self.write(ret_json({'code': 403, 'meg': '你的操作非法'}))
        oq.push(str((id, price)))
        oq.out()
        mystack.delete(id)
        r.delete('token%s' % id)

        # 同步清除缓存
        clear_last(id)

        # 二次清除缓存
        # get_queue(clear_last, id=id)

        drq = DelayRedisQueue("myqueue")

        drq.add(int(id), 10)

        # mydb.update("insert into `order` (orderid,uid,price) values (%s,%s,%s)" % (orderid, id, price))

        return self.write(ret_json({'code': 200, 'meg': '充值成功'}))

    async def get(self):
        orderid = self.get_argument('orderid', None)

        orderid = int(orderid)

        mydb = MysqlDb()

        reslist = "select table_name from information_schema.tables where table_schema='mytronadox' and table_name like 'order_%'"

        c = mydb.read_list(reslist)

        str1 = ''
        # 重组联合查询的sql语句
        for i in c:
            str1 += 'select * from `%s` where orderid = %s union ' % (i['TABLE_NAME'], orderid)

        str1 = str1.rstrip('union ')

        res = mydb.read(str1)

        print(res)

        res['orderid'] = str(res['orderid'])

        return self.write(ret_json(res))


class OrderList(OrderInsert):

    async def get(self):
        # mydb = MysqlDb()
        # r = redis.Redis(decode_responses=True)
        # id = self.get_argument('id', None)
        #
        # page = self.get_argument('page', 1)
        # page = int(page)
        # # print(id)
        #
        # # 设置默认的最后一个订单id
        # last_orderid = 0
        #
        # # 查询当前数据库中所有order_类型的表
        # reslist = "select table_name from information_schema.tables where table_schema='mytronadox' and table_name like 'order_%'"
        #
        # c = mydb.read_list(reslist)
        #
        # str1 = ''
        # sql_list = ''
        #
        # # str_head = 'select count(1) as total from ('
        # last_orderid = r.get('last%s%s' % id, page - 1)
        # # 重组联合查询的sql语句
        # for i in c:
        #     str1 += 'select * from `%s` where uid = %s union ' % (i['TABLE_NAME'], id)
        #
        #     # 判断大于等于第二页情况
        #     if page >= 2:
        #         if last_orderid:
        #             sql_list += 'select * from `%s` where uid = %s and orderid < %s union ' % (i['TABLE_NAME'], id,last_orderid)
        #
        #     else:
        #         sql_list += 'select * from `%s` where uid = %s  union ' % (i['TABLE_NAME'], id)
        #
        #     # str1 += '(select * from `%s` where uid = %s ) union all ' % (i['TABLE_NAME'], id)
        #
        # orderall = str1.rstrip('union ') + ' order by date desc'
        # sql_list = sql_list.rstrip('union')
        #
        #
        #
        # # 总数
        # # orderall = str_head + str1.rstrip('union all ') + ' ) as a'
        #
        # # 所有数据
        # res = mydb.read_list(orderall)
        #
        # # 保存最后一条订单的id
        # last_orderid = res[-1]['orderid']
        #
        #
        #
        # r.set('last%s' % id,last_orderid)
        #
        #
        # # 存储这一次最后一条的查询数据节点
        #
        #
        # # 总条数
        # total = len(res)
        # # total = mydb.read(orderall)
        #
        # # 实例化类  并   传参
        # mypage = Page(total=total, page=page)
        # # mypage = Page(total=total['total'], page=page)
        #
        # # 总共多少页
        # pagenum = mypage.page_num()
        #
        # # 当前页数据
        # order_list = mypage.get_data(orderall)
        #
        # for i in order_list:
        #     i['orderid'] = str(i['orderid'])
        #
        # return self.write(ret_json({'data': order_list, 'page': pagenum, 'pagecount': total}))

        # 用户id
        id = self.get_argument("id", None)

        # 当前页数
        page = int(self.get_argument("page", 1))

        size = 5

        r = redis.Redis(decode_responses=True)

        # 设置默认的最后一个订单id
        last_orderid = 0

        # 实例化对象
        mydb = MysqlDb()

        # 拼接
        sql_head = "select count(1) as total from ("

        sql = ""

        sql_list = ""

        # 遍历
        for x in range(table_size):

            sql += "union all (select *  from order_%s where uid = %s)" % (x + 1, id)

            # 判断
            # 大于等于第二页情况
            if page >= 2:

                last_orderid = r.get("last%s%s" % (id, page - 1))

                # 判断key是否存在
                if last_orderid:

                    sql_list += "union all (select *  from order_%s where uid = %s and orderid < %s )" % (
                        x + 1, id, last_orderid)

                else:

                    sql_list += "union all (select *  from order_%s where uid = %s)" % (x + 1, id)
            # 第一页
            else:
                sql_list += "union all (select *  from order_%s where uid = %s)" % (x + 1, id)

        sql = sql.lstrip("union all")
        sql_list = sql_list.lstrip("union all")

        sql = sql_head + sql + " ) as a "

        # print(sql)

        total = mydb.read(sql)

        # print("一共%s个" % str(total["total"]))

        # print(total["total"] % size)

        # page_num = 0

        if total["total"] % size == 0:
            page_num = str(total["total"] // size)
            # print("一共有%s页" % str(total["total"] // size))

        else:
            page_num = str(total["total"] // size + 1)
            # print("一共有%s页" % str(total["total"] // size + 1))

        start = (page - 1) * size
        end = size

        if page >= 2:

            if last_orderid:

                sql_list += " order by orderid desc limit %s " % (end)

            else:

                sql_list += " order by orderid desc limit %s,%s " % (start, end)


        else:

            sql_list += " order by orderid desc limit %s,%s " % (start, end)

        # print(sql_list)

        # 获取分页后数据

        res = mydb.read_list(sql_list)

        # print(res)

        # 保存最后一条订单的id

        last_orderid = res[-1]["orderid"]

        r.set("last%s%s" % (id, page), last_orderid)

        # 存储这一次最后的查询数据节点

        for i in res:
            i['orderid'] = str(i['orderid'])
            # 总页数      # 当前页信息
        return self.write(ret_json({"total": total["total"], "data": res, "page_num": page_num}))


# 步骤表单
class StepForm(BaseHandler):

    # 出栈
    async def delete(self):
        id = self.get_argument('id', None)

        mystack = MyStack()

        last = mystack.pop(id)

        last = eval(last)

        if last['index'] == 2 and last['value'] == '0':
            r = redis.Redis(decode_responses=True)
            last['card'] = r.get('card%s' % id)

        return self.write(ret_json(last))

    # 获取栈定数据
    async def get(self):

        id = self.get_argument('id', None)

        mystack = MyStack()

        last = mystack.peek(id)

        return self.write(ret_json(last))

    # 入栈
    async def post(self):
        r = redis.Redis(decode_responses=True)
        # 获取用户id
        id = self.get_argument('id', None)

        # 步骤
        num = self.get_argument('num', None)

        # 步骤中的数据(表单数据)
        value = self.get_argument('value', None)

        card = self.get_argument('card', None)

        if int(num) == 2 and int(value) == 0:
            r.set('card%s' % id, card)

        if int(num) >= 3:
            return self.write(ret_json({'code': 403, 'meg': '请勿篡改数据'}))
        mystack = MyStack()

        # 查看站内情况

        mylist = r.lrange(id, 0, -1)

        # 遍历
        for i in mylist:
            i = eval(i)

            # 比对
            if str(num) == str(i['index']):
                return self.write(ret_json({'code': 403, 'meg': '该步骤已经入栈'}))

        # 入栈
        mystack.push(id, '{"index":%s,"value":"%s"}' % (num, value))
        self.write(ret_json({'code': 200, 'meg': '提交步骤成功'}))


# 授权管理
class User_List(BaseHandler):

    async def get(self):
        mydb = MysqlDb()
        res = mydb.read_list("select * from `user`")
        return self.write(ret_json(res))

    async def post(self):
        uid = self.get_argument('uid', None)

        nid = self.get_argument('nid', None)

        acl = ACL()
        acl.set_node(nid, uid)

        return self.write(ret_json({'code': 200, 'meg': '授权成功'}))


# 节点操作
class NodeOperate(BaseHandler):
    async def get(self):
        acl = ACL()
        res = acl.get_node()
        return self.write(ret_json(res))

    async def post(self):
        name = self.get_argument('name', None)

        acl = ACL()
        acl.add_node(name)

        return self.write(ret_json({'code': 200, 'meg': '添加成功'}))

    async def delete(self):
        id = self.get_argument('id', None)

        acl = ACL()
        acl.del_node(id)

        return self.write(ret_json({'code': 200, 'meg': '删除成功'}))

    async def put(self):
        id = self.get_argument('id', None)

        newname = self.get_argument('newname', None)

        acl = ACL()
        acl.edit_node(id, newname)

        return self.write(ret_json({'code': 200, 'meg': '修改成功'}))


# 菜单初始化接口
class Menu(BaseHandler):
    async def get(self):
        id = self.get_argument('id', None)

        acl = RBAC()

        res = acl.get_usernode(id)

        return self.write(ret_json(res))


# 菜单初始化接口
class BinMenu(Menu):
    async def get(self):
        id = self.get_argument('id', None)

        binauth = BinAuth()

        res = binauth.get_usernode(id)

        return self.write(ret_json(res))


# 客服相关接口
class ClientHandler(BaseHandler):

    # 同步客服状态
    async def post(self):
        id = self.get_argument('id', None)

        state = self.get_argument('state', None)

        r = redis.Redis(decode_responses=True)

        r.set('client%s' % id, state)

        return self.write(ret_json({'code': 200, 'meg': '同步状态成功'}))

    # 查询客服
    async def get(self):
        id = self.get_argument('id', None)

        user = await self.application.objects.get(User.select().where(User.id == id))

        self.write(ret_json({'clientid': user.clientid}))

    # 设置客服
    async def put(self):
        # 当前用户id
        id = self.get_argument('id', None)

        # 客服id
        clientid = self.get_argument('clientid', None)

        user = await self.application.objects.get(User.select().where(User.id == id))

        # 设置客服id
        user.clientid = clientid

        await self.application.objects.update(user)

        return self.write(ret_json({'code': 200, 'meg': '设置成功'}))


# 消息类
class Msg:
    # 记录当前链接的用户
    connector = {}

    # 用户加入链接
    def user_connect(self, user):
        if user not in self.connector:
            self.connector[user] = 'user'

    # 断开连接
    def user_del(self, user):
        self.connector.pop(user)

    # 发消息
    def send(self, message, userid, info):
        # info = json.loads(info)
        for user in self.connector:
            user.write_message(ret_json({'meg': message, 'userid': userid, 'info': info, 'code': 200}))


# websocket  主动推送试图
class WsHandler(WsBaseHandler):
    def open(self):
        # self.write_message('你好这里是后端!')

        Msg().user_connect(self)

    def on_close(self) -> None:
        Msg().user_del(self)

    # 接收消息方法
    def on_message(self, message):
        self.write_message(message)


# 客户出队接口
class OutServer(BaseHandler):

    async def get(self):
        # 接收用户id
        id = self.get_argument('id', None)

        # 出队类型
        type = self.get_argument('type', None)

        chatqueue = ChatQueue('chat')

        # 客户出队
        if type == 'server':

            # 获取队列所有数据
            mylist = chatqueue.get()

            if not mylist:
                return self.write(ret_json({'uid': 0}))

            if id == mylist[0]:

                # 出队
                uid = chatqueue.pop(type='server')

            else:

                uid = 0

            return self.write(ret_json({'uid': uid}))
        # 客服出队
        else:

            # 出队
            chatqueue.pop(type='client')

            return False


# 客服主动出发出队接口
class OutClient(BaseHandler):

    async def get(self):
        chatqueue = ChatQueue('chat')

        # 出队
        uid = chatqueue.pop(type='client')

        return self.write(ret_json({'uid': uid}))


# 主动推送消息接口
class Push(BaseHandler):

    async def get(self):
        meg = self.get_argument('meg', None)
        userid = self.get_argument('userid', None)
        info = self.get_argument('info', None)

        key = self.get_argument('key', None)

        chat = Chat()

        # 存入redis消息
        chat.push(key, meg)

        # 发送消息
        Msg().send(meg, userid, info)

        return self.write(ret_json({'code': 200, 'meg': '发送消息成功'}))


# 聊天记录信息接口
class ChatList(Push):
    async def get(self):
        key = self.get_argument('key', None)

        # 当前用户id
        id = self.get_argument('id', None)

        if id is not None:
            # 入队
            chatqueue = ChatQueue('chat')

            chatqueue.push(id)

        # 获取存入redis聊天记录
        chat = Chat()
        chatlist = chat.get(key)

        return self.write(ret_json(chatlist))


# 定义token装饰器
def before_request(func):
    @wraps(func)
    def wrapper(self, *args, **kwargs):
        id = self.get_argument('id', None)
        token = self.get_argument('token', None)

        if token is None:
            return self.write(ret_json({'code': 403, 'meg': '您未登录'}))
        token = jwt.decode(token, 'DragonK', algorithms=['HS256'])
        # print(token['id'])
        if token['id'] != int(id):
            return self.write(ret_json({'code': 403, 'meg': '您的密钥非法'}))

        return func(self, *args, **kwargs)

    return wrapper


# 审核用户信息
class PutAudit(BaseHandler):
    async def put(self):
        type = self.get_argument('type', None)
        id = self.get_argument('id', None)
        user = await self.application.objects.get(User, id=id)
        if type == '1':
            user.state = 2
            await self.application.objects.update(user)
            msg = "审核拒绝"
            Msg().send(msg, id, json.dumps({"state": user.state}))
            return self.write(ret_json({"code": 403, "meg": "拒绝"}))
        else:
            user.state = 1
            await self.application.objects.update(user)
            msg = "审核通过"
            Msg().send(msg, id, json.dumps({"state": user.state}))
            return self.write(ret_json({"code": 200, "meg": "通过"}))


# 审核接口/运营接口
class UserAudit(BaseHandler):
    @before_request
    async def get(self):
        # 审核员
        id = self.get_argument('id', None)
        type = self.get_argument('type', None)
        page = self.get_argument('page', 1)
        if (int(type) > 3) or (int(type) < 0):
            return self.write(ret_json({'code': 403, 'meg': '请不要篡改用户信息'}))

        # 出队mysql
        # mq = MyQueueSort()
        # try:
        #     mq.pop(int(id))
        # except Exception as e:
        #     pass

        # 出队mongo
        # mq = MyMongo_QueueSort()
        # try:
        #     mq.pop(int(id))
        # except Exception as e:
        #     pass

        # 出队redis
        # rq = RedisQueue('rq')
        # rq.get_wait(timout=None, auid=int(id))

        # 方法1    线程     代码实现
        # mydb = MysqlDb()
        # res = mydb.read_list("select id from `user` where type = 2")
        #
        # threads = []
        # for i in res:
        #     t = threading.Thread(target=doout, args=(i['id'],))
        #     threads.append(t)
        #
        # [i.start() for i in threads]
        # [i.join() for i in threads]

        # 方法2    线程    调用文件夹
        # try:
        #     os.system('out_queue.py')
        # except Exception as e:
        #     pass

        # 方法3   进程   调用文件夹
        # try:
        #     os.system('test_process.py')
        # except Exception as e:
        #     pass

        # 方法4 协程
        # try:
        #     os.system('out_queue_async.py')
        # except Exception as e:
        #     pass

        user = await self.application.objects.execute(
            User.select().where((User.state == int(type)) & (User.id != id) & (User.auid == id)).order_by(
                User.id.desc()).paginate(
                int(page), 1))
        total = await self.application.objects.count(
            User.select().where((User.state == int(type)) & (User.id != id) & (User.auid == id)))
        data = [myser(i) for i in user]
        # print(data)
        return self.write(ret_json({'code': 200, 'msg': "获取成功", 'data': data, 'total': total}))


# 表单结果入库
class AuditUpdate(BaseHandler):
    @before_request
    async def get(self):
        id = self.get_argument('id', None)

        user = await self.application.objects.get(User.select().where(User.id == id))
        # 序列化
        user = myser(user)
        return self.write(ret_json(user))

    async def put(self):
        id = self.get_argument("id", None)

        audit = self.get_argument("audit", None)

        user = await self.application.objects.get(User.select().where(User.id == id))

        # 赋值
        user.audit = audit
        user.state = 3

        await self.application.objects.update(user)

        # mysql入队
        # mq = MyQueueSort()
        # mq.push(int(id))

        # mongo入队
        # mq = MyMongo_QueueSort()
        # mq.push(int(id))

        # redis入队
        rq = RedisQueue('rq')
        mylist = rq.get_lrange(id)

        if str(id) not in mylist:

            if user.level == 0:
                rq.put_r(int(id))
            else:
                rq.put_vip('rq_vip', int(id))

            # try:
            #     os.system('test_request.py')
            # except Exception as e:
            #     pass
            # res = requests.get('https://localhost/push/', params={'meg': '您有新的数据'}, verify=False)

            msg = Msg()

            msg.send('您有新的审批数据', {'username': user.username, 'type': user.type, 'userid': user.id}, audit)

            return self.write(ret_json({"code": 200, "meg": "提交审核资料成功"}))
        return self.write(ret_json({"code": 200, "meg": "提交审核资料成功"}))


# 文件上传
class Upload(BaseHandler):

    async def post(self):
        file = self.request.files['file']

        type = self.get_argument('type', None)

        for meta in file:
            filename = meta['filename']

            with open('./static/' + filename, 'wb') as f:
                f.write(meta['body'])

            if type == 'audit':
                return self.write(ret_json({'code': 200, 'meg': '上传审批成功', 'filename': filename}))

            else:
                return self.write(ret_json({'code': 200, 'meg': '上传广告素材成功', 'filename': filename}))


# 权限鉴权装饰器
def auth_request(func):
    @wraps(func)
    def wraper(self, *args, **kwargs):
        # 设置响应
        id = self.get_argument('id', None)
        nodeid = self.get_argument('nodeid', None)
        print('rbac鉴权装饰器')

        # rbac = RBAC()
        # res = rbac.get_nodeuser(int(nodeid), id)

        # 资源路径
        # urilist = rbac.get_nodeid_uri(self.request.uri)

        binauth = BinAuth()
        res = binauth.auth_node(int(id), int(nodeid))

        if not res:
            return self.write(ret_json({'code': 403, 'meg': '无权限访问'}))

        response = func(self, *args, **kwargs)

        return response

    return wraper


# abac属性权限鉴定装饰器
def abac_request(func):
    @wraps(func)
    def wraper(self, *args, **kwargs):
        print('abac属性装饰器')

        id = self.get_argument('id', None)

        mydb = MysqlDb()
        user = mydb.read("select type,sex,age from user where id = %s" % id)

        abac = ABAC()
        res = abac.get_role(user['type'])

        value = res.get('sex', None)

        if value:
            if int(res['sex']) != int(user['sex']):
                return self.write(ret_json({'code': 403, 'meg': '只能女性上传'}))

        value = res.get('age', None)

        if value:
            res['age'] = eval(res['age'])

            # 遍历

            # 标识
            age_key = 0

            for i in res['age']:

                if user['age'] >= i[0] and user['age'] <= i[1]:
                    age_key = 1
                    break

            if not age_key:
                return self.write(ret_json({'code': 403, 'meg': '年龄超出限制'}))

        response = func(self, *args, **kwargs)

        return response

    return wraper


# 广告物料上传
class AdUpload(Upload):
    @before_request
    @auth_request
    @abac_request
    async def post(self):
        file = self.request.files['file']
        id = self.get_argument('id', None)

        for meta in file:
            filename = meta['filename']
            filename = "%s_%s" % (id, filename)

            with open("./static/" + filename, 'wb') as f:
                f.write(meta['body'])
        return self.write(ret_json({'code': 200, 'filename': filename, 'meg': '上传物料文件成功'}))

    async def get(self):
        id = self.get_argument('id', None)

        filelist = os.listdir('./static/')

        file = []
        for i in filelist:
            if re.match('\d*', i).group() == id:
                file.append({'filename': 'https://localhost/static/' + i, 'type': os.path.splitext(i)[1][1:]})
        # print(file)

        return self.write(ret_json({'code': 200, 'filelist': file}))


# 获取用户角色配置

class UserRole(BaseHandler):

    # @before_request
    async def get(self):
        type = self.get_argument('type', None)

        if type:
            # 查询
            try:
                role = await self.application.objects.get(Role, id=type)
                return self.write(ret_json(role.audit))
            except Exception as e:
                return self.write(ret_json({'code': 403, 'meg': '用户类型错误'}))

        roles = await self.application.objects.execute(Role.select())

        data = [myser(i) for i in roles]

        return self.write(ret_json(data))


# 用户视图
class UserInfo(BaseHandler):

    def make_password(self, mypass):

        md5 = hashlib.md5()

        # 转码
        sign_utf8 = str(mypass + "123").encode(encoding="utf-8")
        md5.update(sign_utf8)

        return md5.hexdigest()

    # 注册
    async def post(self):

        # 接收参数  账密
        username = self.get_argument("username", None)

        password = self.get_argument("password", None)

        type = self.get_argument('type', None)

        # level = self.get_argument('level', None)

        # 开始匹配
        if username is None or password is None or username == '' or password == '':
            return self.write(ret_json({'code': 403, 'meg': '您的用户名或密码不合法'}))

        # 入库操作
        try:

            await self.application.objects.create(User, username=username,
                                                  password=self.make_password(password), type=type)
            # , level=level)

        except Exception as e:

            return self.write(ret_json({"code": 403, "meg": "您注册的账户已经被占用"}))

        return self.write(ret_json({"code": 200, "meg": "注册成功"}))

    async def get(self):
        # 接收参数  账密
        username = self.get_argument("username", None)

        password = self.get_argument("password", None)
        # 身份认证登录
        type = self.get_argument('type', None)

        print(username, password, type)

        # 开始匹配
        if username is None or password is None or username == '' or password == '':
            return self.write(ret_json({'code': 403, 'meg': '您的用户名或密码不合法'}))

        try:
            user = await self.application.objects.get(User, username=username,
                                                      password=self.make_password(password),
                                                      type=type)
            print(user)

            # mydb = MysqlDb()
            # pas = self.make_password(password)

            # sql = (('select * from `user` where username = %s and password = %s and type = %s') %(username, pas, type))
            # print(sql)
            # user = mydb.read(sql)

            # sql = ('select * from `user` where username = %s and password = %s and type = %s')
            # print(sql)
            # user = mydb.read(sql,(username, pas, type))
            #
            # print(user)

            # 生成token
            token = jwt.encode({'id': user.id}, 'DragonK', algorithm='HS256')

            token = str(token, encoding='utf-8')

            print(token)

            # return self.write(ret_json({'code': 200, 'meg': '登录成功'}))

        except Exception as e:

            return self.write(ret_json({'code': 403, 'meg': '您的用户名或密码错误'}))

        return self.write(
            ret_json({'code': 200, 'meg': '登录成功', 'uid': user.id, 'type': user.type, 'username': user.username,
                      'token': token}))


import qrcode


def trans_xml_to_dict(data_xml):
    soup = BeautifulSoup(data_xml, features='xml')
    xml = soup.find('xml')  # 解析XML
    if not xml:
        return {}
    data_dict = dict([(item.name, item.text) for item in xml.find_all()])
    return data_dict


def trans_dict_to_xml(data_dict):  # 定义字典转XML的函数
    data_xml = []
    for k in sorted(data_dict.keys()):  # 遍历字典排序后的key
        v = data_dict.get(k)  # 取出字典中key对应的value
        if k == 'detail' and not v.startswith('<![CDATA['):  # 添加XML标记
            v = '<![CDATA[{}]]>'.format(v)
        data_xml.append('<{key}>{value}</{key}>'.format(key=k, value=v))
    return '<xml>{}</xml>'.format(''.join(data_xml))  # 返回XML


def get_sign(data_dict, key):  # 签名函数，参数为签名的数据和密钥
    params_list = sorted(data_dict.items(), key=lambda e: e[0], reverse=False)  # 参数字典倒排序为列表
    params_str = "&".join(u"{}={}".format(k, v) for k, v in params_list) + '&key=' + key
    # 组织参数字符串并在末尾添加商户交易密钥
    md5 = hashlib.md5()  # 使用MD5加密模式
    md5.update(params_str.encode())  # 将参数字符串传入
    sign = md5.hexdigest().upper()  # 完成加密并转为大写
    return sign


from io import BytesIO


class WX_pay(BaseHandler):

    # def __init__(self,key,appid,mch_id,body):
    #     self.key = key # 商户api秘钥`
    #     self.appid = appid
    #     self.mch_id = mch_id
    #     self.body = body  #  商品描述

    def get(self):
        url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'  # 微信扫码支付接口
        key = '945bec9df3614cffb74e39aba8fbf7d7'  # 商户api秘钥
        total_fee = 1  # 支付金额，单位分
        body = '123123'  # 商品描述
        out_trade_no = 'order_%s' % random.randrange(100000, 999999)  # 订单编号
        params = {
            'appid': 'wx092344a76b9979ff',  # APPID
            'mch_id': '1602932608',  # 商户号
            'notify_url': 'http://wxpay.v3u.cn/wx_back/',  # 支付域名回调地址
            'product_id': 'goods_%s' % random.randrange(100000, 999999),  # 商品编号
            'trade_type': 'NATIVE',  # 支付类型（扫码支付）
            'spbill_create_ip': '114.254.176.137',  # 发送请求服务器的IP地址
            'total_fee': total_fee,  # 订单总金额
            'out_trade_no': out_trade_no,  # 订单编号
            'body': body,  # 商品描述
            'nonce_str': 'ibuaiVcKdpRxkhJA'  # 字符串
        }
        sign = get_sign(params, key)  # 获取签名
        params.setdefault('sign', sign)  # 添加签名到参数字典
        xml = trans_dict_to_xml(params)  # 转换字典为XML
        response = requests.request('post', url, data=xml)  # 以POST方式向微信公众平台服务器发起请求
        data_dict = trans_xml_to_dict(response.content)  # 将请求返回的数据转为字典
        print(data_dict)
        # qrcode_name = out_trade_no + '.png'  # 支付二维码图片保存路径
        if data_dict.get('return_code') == 'SUCCESS':  # 如果请求成功
            stream = BytesIO()
            img = qrcode.make(data_dict.get('code_url'))  # 创建支付二维码片

            img.save(stream, "png")
            # img.save('./' + qrcode_name)  # 保存支付二维码

            imgs = stream.getvalue()
            self.set_header("Content-Type", "image/png")
            return self.write(imgs)
